package net.xuele.learn.future;

import org.junit.Test;

import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by huanghao（HIKARI） on 2019/8/1 0001.
 */
public class Future {


    @Test
    public void testDebug() {
        CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello";
        }).thenAccept(v -> {
            System.out.println("v=" + v);
        });
    }

    public static void main(String[] args) {
        // 1、变换结果：这些方法的输入是上一个阶段计算后的结果，返回值是经过转化后结果
        String join = CompletableFuture.supplyAsync(() -> "hello,").thenApply(x -> x + "world").join();
        System.out.println(join);
        // 2、消费结果：这些方法只是针对结果进行消费，入参是Consumer，没有返回值

        CompletableFuture.supplyAsync(() -> "hello").thenAccept(v -> {
            System.out.println("v=" + v);
        });


        String join1 = CompletableFuture.supplyAsync(() -> {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "Hello";
                }
        ).thenCombine(CompletableFuture.supplyAsync(() -> {
            try {

                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "world";
        }), (s1, s2) -> s1 + "--" + s2).join();
        System.out.println(join1);
    }

    /**
     * 虽然Future以及相关使用方法提供了异步执行任务的能力，但是对于结果的获取却是很不方便，
     * 只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背，轮询的方式又会耗费无谓的CPU资源，而且也不能及时地得到计算结果
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Test
    public void fun() throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        FutureTask<String> stringFutureTask = new FutureTask<>(() -> {
            Thread.sleep(2000);
            return "hello";
        });
        executorService.submit(stringFutureTask);
        long start = System.currentTimeMillis();
        String s = stringFutureTask.get();
        System.out.println("耗时为：" + (System.currentTimeMillis() - start) + "，结果为：" + s);
        java.util.concurrent.Future<String> submit = executorService.submit(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "kkkk";
        });
        long l = System.currentTimeMillis();
        System.out.println("开始获取S1的结果");
        String s1 = submit.get();
        System.out.println("耗时为：" + (System.currentTimeMillis() - l) + "，结果为：" + s1);

    }


    private static Random rand = new Random();
    private static long t = System.currentTimeMillis();

    static int getMoreData() {
        System.out.println("begin to start compute");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int i = rand.nextInt(1000);
        System.out.println("end to start compute. passed " + (System.currentTimeMillis() - t) + " milliseconds" + ",result = " + i);
        return i;
    }

    /**
     * CompletableFuture继承自Future接口
     * <p>
     * Runnable类型的参数会忽略计算的结果，
     * Consumer是纯消费计算结果，
     * BiConsumer会组合另外一个CompletionStage纯消费，
     * Function会对计算结果做转换，
     * BiFunction会组合另外一个CompletionStage的计算结果做转换
     */
    @Test
    public void completeFuture() throws ExecutionException, InterruptedException {
        //CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //    int i = 1/0;
        //    return 100;
        //});
        ////future.join();
        //future.get();

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(
                () -> {
                    System.out.println(Thread.currentThread().getName());
                    return getMoreData();
                }
        );
        // 消费，无返回值，对原来的计算结果无影响
        //thenAcceptBoth以及相关方法提供了类似的功能，当两个CompletionStage都正常完成计算的时候，就会执行提供的action，它用来组合另外一个异步的结果。
        //runAfterBoth是当两个CompletionStage都正常完成计算的时候,执行一个Runnable，这个Runnable并不使用计算的结果。
        CompletableFuture<Void> thenAccept = future.thenApplyAsync((a) -> {
                    System.out.println("thenAccept " + a + " " + Thread.currentThread().getName());
                    return a;
                }

        ).thenAcceptBoth(CompletableFuture.supplyAsync(() -> 7), (a, b) -> {
            System.out.println("thenAcceptBoth:" + a + " " + (a + b));
        });
//        CompletableFuture<Void> thenAcceptBoth = future.thenAcceptBoth(CompletableFuture.supplyAsync(() -> 7), (a, b) -> {
//            System.out.println("thenAcceptBoth:" +a+" "+ (a + b));
//        });
        System.out.println("00000");
        System.out.println(thenAccept.get());
        System.out.println("00000");
//        // 转换，对上一步的结果进行操作
//        CompletableFuture<String> thenApply = future.thenApplyAsync((v) -> v * 10).thenApply(Object::toString);
//        // 计算完成时的处理，此处对结果进行操作，将不会产生任何影响
//        CompletableFuture<String> whenComplete = thenApply.whenComplete((v, e) -> {
//            v = v + "----";
//            System.out.println(v);
//            System.out.println(e);
//        });
//        long l = System.currentTimeMillis();
//        String s = whenComplete.get();
//        System.out.println("耗时为：" + (System.currentTimeMillis() - l) + "，结果为：" + s);
    }

    /**
     * public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
     * public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
     * public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
     * 这一组方法接受一个Function作为参数，这个Function的输入是当前的CompletableFuture的计算值（必须得等第一个计算完成之后才可以进行第二个计算）
     * 返回结果将是一个新的CompletableFuture，这个新的CompletableFuture会组合原来的CompletableFuture和函数返回的CompletableFuture。
     * <p>
     * thenCompose返回的对象并不一是函数fn返回的对象，如果原来的CompletableFuture还没有计算出来，它就会生成一个新的组合后的CompletableFuture
     */
    @Test
    public void thenCompose() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future1");
            return "100";
        });
        CompletableFuture<String> thenCompose = future.thenCompose(i -> CompletableFuture.supplyAsync(() -> {
            System.out.println("future2");
            return (i + 10);
        }));
        System.out.println(thenCompose.get());
    }

    /**
     * 两个CompletionStage是并行执行的，它们之间并没有先后依赖顺序，other并不会等待先前的CompletableFuture执行完毕后再执行
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Test
    public void thenCombine() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("11111");
            return 100;
        });
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("22222");
            return "abc";
        });
        CompletableFuture<String> thenCombine = future.thenCombine(future1, (a, b) -> {
            return a + b;
        });

        System.out.println(thenCombine.get());
        System.out.println("start");
    }


    @Test
    public void testCombine() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> integer = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        });

        CompletableFuture<String> string = CompletableFuture.supplyAsync(() -> {
            return "2";
        });

        CompletableFuture<Long> longF = string.thenCombine(integer, (string1, integer1) -> {
            System.out.println(string1 + integer1);
            return Long.parseLong(string1 + integer1);
        });

        System.out.println(longF.get());
    }

    /**
     * exceptionally方法返回一个新的CompletableFuture，当原始的CompletableFuture抛出异常的时候，就会触发这个CompletableFuture的计算，
     * 调用function计算值，否则如果原始的CompletableFuture正常计算完后，这个新的CompletableFuture也计算完成，它的值和原始的CompletableFuture的计算的值相同。
     * 也就是这个exceptionally方法用来处理异常的情况
     */
    @Test
    public void exception() throws ExecutionException, InterruptedException {
        CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {
            if (true) {
                throw new RuntimeException("FAKE");
            }
            return "OK";
        }).exceptionally(e -> {
            System.out.println(e.getMessage());
            return "Hello world!";
        });
        System.out.println(exceptionally.get());
    }


    @Test
    public void either() throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "2";
        });

        //future1.acceptEither(future2, (i) -> {
        //    System.out.println("3333" + i);
        //});

        CompletableFuture<String> applyToEither = future1.applyToEither(future2, i -> i + i);
        System.out.println(applyToEither.get());
    }

}
